#include <gtk/gtk.h>
#include "post.h"
#include "postwin.h"
#include <json-glib/json-glib.h>
#include <glib/gstdio.h>
#define CONFIG_FILE		"config/net_assist.json"
#define TMP_STATUS_OPENED	"server opened"
#define TMP_STATUS_CLOSED	"server closed"
#define TMP_BUTTON_CLOSE	"close server"
#define TMP_BUTTON_OPEN		"open server"
#define TMP_BUTTON_CONNECT	"connect"
#define TMP_BUTTON_DISCONNECT	"disconnect"
struct _PostWindow
{
  GtkApplicationWindow parent;
};

typedef struct _PostWindowPrivate PostWindowPrivate;

struct _PostWindowPrivate
{
	GtkWidget *method;
	GtkWidget *url;
	GtkWidget *local_port;

	GtkWidget *enter;

	GtkWidget *input;
	GtkWidget *hex;

	GtkWidget *msg_scrolled;
	GtkWidget *msg_box;

	GtkWidget *status;
  GSettings *settings;
	gpointer socket;
};

G_DEFINE_TYPE_WITH_PRIVATE(PostWindow, post_window, GTK_TYPE_APPLICATION_WINDOW);

void *update_config(const char *filename, const char *key, const char *value);
void *sys_uint8_print(const char *name, const guint8 *buf, int len)
{
	int i;

	printf("[%4d]%16s", len, name);
	for (i = 0; i < len; i ++ ) {
		printf("%02x ", buf[i]);
	}
	printf("\n");

	return NULL;
}
int sys_str_to_uint8_calloc(const char *src, guint8 **dst_p)
{
        int rc = 0, i, src_len = 0, dst_len = 0;
	guint8 *dst = NULL, h1, h2;

	if (src == NULL) {
		g_print("src is NULL\n");
		rc = -1;
		goto err;
	}
	src_len = strlen(src);
	if (src[src_len - 1] == ' ') {
		dst_len = src_len/3;
	} else {
		dst_len = (src_len + 1)/3;
	}
	dst = calloc(1, dst_len);
        for (i = 0; i < dst_len; i++) {
                h1 = src[3*i];
                h2 = src[3*i+1];
		if ((h1 > 47) && (h1 < 58)) {
			h1 -= 48;
		} else if ((h1 > 64) && (h1 < 71)){
			h1 -= 55;
		} else if ((h1 > 96) && (h1 < 103)){
			h1 -= 87;
		}
		if ((h2 > 47) && (h2 < 58)) {
			h2 -= 48;
		} else if ((h2 > 64) && (h2 < 71)){
			h2 -= 55;
		} else if ((h2 > 96) && (h2 < 103)){
			h2 -= 87;
		}
                dst[i] = h1*16 + h2;
        }
err:
	*dst_p = dst;
	rc = dst_len;
	return rc;
}
char *sys_guint8o_str_calloc(const guint8 *msg, int len)
{
	char *res = NULL;
	int size = 0, i = 0;

	size = len * 3;
	res = calloc(1, size + 1);
	for (i = 0; i < len; i++) {
		snprintf(res + (i * 3), 4, "%02x ", msg[i]);
	}
	return res;
}
void *msg_add(gpointer data, const gchar *name, const gchar *msg, gboolean dir)
{
	gchar *show = NULL;
	GtkWidget *msg_box, *record_box, *id_label, *msg_label, *msg_scrolled;
	PostWindow *win;
	PostWindowPrivate *priv;

	win = data;
	priv = post_window_get_instance_private (win);
	msg_scrolled = priv->msg_scrolled;
	msg_box = priv->msg_box;

	g_print("msg show:%ld:%s\n", strlen(msg), msg);
	record_box = GTK_WIDGET(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2));
	gtk_container_add(GTK_CONTAINER(msg_box), GTK_WIDGET(record_box));
/*
	sys_time_uint64_to_str(time_uint64, time_str, sizeof(time_str));
	label = GTK_WIDGET(gtk_label_new(time_str));
	gtk_label_set_width_chars(GTK_LABEL(label), 8);
	gtk_container_add(GTK_CONTAINER(record_box), GTK_WIDGET(label));
*/
	id_label = GTK_WIDGET(gtk_label_new(name));
	gtk_label_set_width_chars(GTK_LABEL(id_label), 8);

  show = g_strdup(msg);
	msg_label = GTK_WIDGET(gtk_label_new(show));
  gtk_widget_set_hexpand(msg_label, TRUE);
  if (dir)
  {
    gtk_container_add(GTK_CONTAINER(record_box), GTK_WIDGET(id_label));
    gtk_container_add(GTK_CONTAINER(record_box), GTK_WIDGET(msg_label));
  } else {
    /* change text background color
    PangoAttrList *attrs;
    PangoAttribute *attr;
    attrs = pango_attr_list_new ();
    attr = pango_attr_background_new (0, 0xffff, 0xffff/2);
    pango_attr_list_insert (attrs, attr);
    gtk_box_set_attributes (GTK_BOX(record_box), attrs);
    pango_attr_list_unref (attrs);
    */
    gtk_widget_set_name (record_box, "me");
    gtk_container_add(GTK_CONTAINER(record_box), GTK_WIDGET(msg_label));
    gtk_container_add(GTK_CONTAINER(record_box), GTK_WIDGET(id_label));

  }

    GtkCssProvider *provider;
    char *css;

    css = g_strdup_printf ("#me {background-color: green;}");
    g_debug ("css:%s\n", css);
    provider = gtk_css_provider_new ();
    gtk_css_provider_load_from_data (provider, css, -1, NULL);
    gtk_style_context_add_provider (gtk_widget_get_style_context (record_box),
          GTK_STYLE_PROVIDER (provider),
          GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
	gtk_widget_show_all(msg_scrolled);
	goto err;
err:
	if (show)
		g_free(show);
	return NULL;
}
typedef struct
{
  GCancellable *cancellable;
  gboolean completed;
} EventCallbackData;
typedef struct {
  GSocketConnection *conn;
  guint8 *data;
  gpointer user_data;
} ReadAsyncData;

gpointer read_thread(gpointer user_data)
{
	int rc = 0, status = 0;
	PostWindow *win;
	//PostWindowPrivate *priv;
	GSocketConnection *conn;
	GInputStream *istream;
	GError *error = NULL;
	gint nread;
	guint8 msg[4096] = {0};
  char *data = NULL;

	win = user_data;
	//priv = post_window_get_instance_private (win);

	while (!(conn = g_object_get_data(G_OBJECT(win), "conn")))
  {
    g_debug("%s:%s[%d]wainting conn\n", __FILE__, __func__, __LINE__);
    sleep (1);
  }
	g_debug("%s:%s[%d]conn:%p\n", __FILE__, __func__, __LINE__, conn);
	istream = g_io_stream_get_input_stream (G_IO_STREAM (conn));
	while (1) {
    if (!(g_object_get_data(G_OBJECT(win), "conn")))
      goto out;
		nread = g_input_stream_read(istream, msg, sizeof(msg) - 1, NULL, &error);
		if (error) {
			g_error("%s\n", error->message);
			goto out;
		}
		if (nread > 0) {
			g_print("++++:%s\n", msg);
			msg_add(win, "peer", (char *)msg, TRUE);
		//	rc = account_client_message_parse(msg, nread, &method, &status, NULL);
			if (!rc) {
        //g_settings_set_int(priv->settings, "status", status);
				if (status == 0) {
				} else {
				}
//				gtk_label_set_text(GTK_LABEL(priv->notice),	account_status[status]);
			}
			memset(msg, 0, sizeof(msg));
		}
	}
out:
	if (error) {
		g_print ("%s\n", error->message);
		g_error_free (error);
	}
	if (data)
		g_free(data);
	g_debug("%s:%s[%d]out\n", __FILE__, __func__, __LINE__);
	return NULL;
}
void socket_client_event (GSocketClient      *client,
               GSocketClientEvent  event,
               GSocketConnectable *connectable,
               GIOStream          *connection,
               gpointer            user_data)
{
	static GEnumClass *event_class;
	gint64 now_us;

	if (!event_class)
		event_class = g_type_class_ref (G_TYPE_SOCKET_CLIENT_EVENT);

	now_us = g_get_real_time ();
	g_print ("%" G_GINT64_FORMAT " GSocketClient => %s [%s]\n",
		now_us,
		g_enum_get_value (event_class, event)->value_nick,
		connection ? G_OBJECT_TYPE_NAME (connection) : "");
	if (event == G_SOCKET_CLIENT_CONNECTED) {
		g_thread_new("client", read_thread, user_data);
  /*
	} else if (event == G_SOCKET_CLIENT_DISCONNECTED) {
  if (connection)
    {
      if (!g_io_stream_close (G_IO_STREAM(connection), NULL, &error))
      {
        g_printerr ("Error closing connection: %s\n",
              error->message);
      }
      g_object_unref (connection);
    } else {
      if (!g_socket_close (G_SOCKET(socket), &error))
      {
        g_printerr ("Error closing socket: %s\n",
              error->message);
      }
    }
  g_object_unref (socket);
  g_clear_error (&error);
  */
  }
}
GMainLoop *loop;
gboolean verbose = FALSE;
gboolean non_blocking = FALSE;
gboolean use_udp = FALSE;
int cancel_timeout = 0;
int read_timeout = 0;
gboolean unix_socket = FALSE;
gboolean tls = FALSE;
//#include "socket-common.c"

static void http_client_request(const char *url, const char *method,
    const char *header, const char *msg, void *user_data)
{
	GSocketClient *socket;
	GSocketConnection *connection;
  GOutputStream   *ostream;
  GError          *error = NULL;
  GString *s = NULL;
  GUri *uri = NULL;
  const gchar *path = NULL;

	g_debug("%s[%d]url:%s,method:%s,msg:%s\n", __FILE__, __LINE__, url, method, msg);

	socket = g_socket_client_new ();
  if (!socket)
  {
    g_error ("socket new\n");
    goto out;
  }
  uri = g_uri_parse (url, G_URI_FLAGS_NONE, &error);
  path = g_uri_get_path (uri);
	g_debug("%s:%s[%d]path:%s\n", __FILE__, __func__, __LINE__,  path);
	g_signal_connect (socket, "event", G_CALLBACK (socket_client_event), user_data);
  connection = g_socket_client_connect_to_uri(socket, url, 0, NULL, &error);
  if (error)
  {
    g_error ("connection:%s\n", error->message);
    goto out;
  }
	g_object_set_data(G_OBJECT(user_data), "conn", connection);
	g_debug("%s:%s[%d]conn:%p\n", __FILE__, __func__, __LINE__, connection);

  ostream = g_io_stream_get_output_stream (G_IO_STREAM (connection));
  s = g_string_new ("");
  g_string_append_printf (s, "%s %s HTTP/1.1\r\n", method, path);
  g_string_append_printf (s, "HOST: %s\r\n", "127.0.0.1:16667");
  g_string_append (s, "Content-Type: application/json;charset=UTF-8\r\n");
  g_string_append (s, "Accept: */*\r\n");
  g_string_append_printf (s, "Content-Length: %"G_GINT64_FORMAT"\r\n",
      strlen(msg));
  g_string_append (s, "\r\n");
  if (msg)
    g_string_append (s, msg);
  if (g_output_stream_write_all (ostream, s->str, s->len, NULL, NULL, NULL))
    {
    }
  //ensure_connection_condition (G_IO_STREAM (connection), G_IO_OUT, NULL);
  if (error) {
    g_debug ("%s[%d]error:%s\n", __FILE__, __LINE__, error->message);
    g_error_free (error);
  }
	msg_add(user_data, "me", s->str, FALSE);
out:
  if (uri)
    g_uri_unref(uri);
  if (s)
    g_string_free (s, TRUE);
  return;
}
static void enter_cb(GObject *go, gpointer user_data)
{
	PostWindow *win;
	PostWindowPrivate *priv;
	GtkTextBuffer *buffer;
	GtkTextIter start, end;
	//GSocketConnection *conn;
	//GError *error = NULL;
	gint hex, send_len = 0;
	const gchar *msg = NULL;//, *method = NULL;
	guint8 *send_buf = NULL, *show = NULL;

	win = user_data;
	priv = post_window_get_instance_private (win);
	buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(priv->input));
	gtk_text_buffer_get_bounds(buffer, &start, &end);
	msg = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
	//conn = priv->socket;
	hex = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->hex));
	if (hex) {
		send_len = sys_str_to_uint8_calloc(msg, &send_buf);
	} else {
		send_buf = (guint8 *)g_strdup(msg);
		send_len = strlen(msg);
	}
	sys_uint8_print("----:", send_buf, send_len);
	//method = gtk_combo_box_get_active_id(GTK_COMBO_BOX(priv->method));
  http_client_request(g_settings_get_string(priv->settings, "url"),
      g_settings_get_string(priv->settings, "method"),
      NULL, msg, win);
	goto err;
err:
	if (show)
		g_free(show);
	if (send_buf)
		g_free(send_buf);
  //sleep(5);
}
void *update_config(const char *filename, const char *key, const char *value)
{
	gint add = 0;
	JsonParser *parser = NULL;
	JsonNode *root = NULL, *node = NULL;
	JsonObject *obj = NULL;
	JsonGenerator *generator = NULL;
	GError *error = NULL;
	const gchar *obj_str = NULL;

	if (g_access(filename, F_OK)) {
		g_print ("file %s not exist\n", filename);
		generator = json_generator_new();
		obj = json_object_new();
		root = json_node_alloc();
		json_node_init_object(root, obj);
		json_object_set_string_member(obj, key, value);
		json_generator_set_root(generator, root);
		json_generator_to_file(generator, filename, &error);
		if (error) {
			g_print ("Unable to parse `%s': %s\n",
					filename, error->message);
			g_error_free (error);
			g_object_unref (parser);
			goto err;
		}
	} else {
		//g_print ("file %s exist\n", filename);
		parser = json_parser_new ();
		json_parser_load_from_file (parser, filename, &error);
		if (error) {
			g_print ("Unable to parse `%s': %s\n",
					filename, error->message);
			g_error_free (error);
			g_object_unref (parser);
			goto err;
		}
		root = json_parser_get_root (parser);
		obj = json_node_get_object (root);
		g_assert(obj != NULL);
		node = json_object_get_member(obj, key);
		if (node == NULL) {
			add = 1;
			//json_node_init_string(node, value);
		} else{
			obj_str = json_object_get_string_member(obj, key);
			g_print("reset config:%s:%s\n", key, obj_str);
			if (g_strcmp0(obj_str, value)) {
				add = 1;
			}
		}
		if (add) {
			json_object_set_string_member(obj, key, value);
			generator = json_generator_new();
			json_generator_set_root(generator, root);
			json_generator_to_file(generator, filename, &error);
			if (error) {
				g_print ("Unable to parse `%s': %s\n",
						filename, error->message);
				g_error_free (error);
				g_object_unref (parser);
				goto err;
			}
		}
	}
err:
	if (root)
		json_node_free(root);
	return error;
}
/*
static void c_cb(GObject *go, gpointer user_data)
{
	PostWindow *win;
	PostWindowPrivate *priv;
	const gchar *ip, *port_str, *protocol = NULL, *status = NULL,
	      *connect = NULL;
	gint port = 0, local_port = 0;
	gboolean rc = TRUE;
	GSocketClient *client;
	GSocket *socket = NULL;
	GError *error = NULL;
	GSocketService *service;
	GSocketConnectable *addr_s;
	GSocketConnection *conn;
	GSocketAddress *addr;

	win = user_data;
	priv = post_window_get_instance_private (win);

	port_str = gtk_entry_get_text(GTK_ENTRY(priv->port));
	port = atoi(port_str);
	update_config(CONFIG_FILE, "port", port_str);

	port_str = gtk_entry_get_text(GTK_ENTRY(priv->local_port));
	local_port = atoi(port_str);
	update_config(CONFIG_FILE, "local_port", port_str);

	protocol = gtk_combo_box_get_active_id(GTK_COMBO_BOX(priv->method));

	//printf("sf\n\n");
	g_print("ip:%s\tport:%d\n", ip, port);
	//client

	status = gtk_label_get_text(GTK_LABEL(priv->status));
	if (!g_strcmp0(protocol, "udp")) {
		socket = g_socket_new(G_SOCKET_FAMILY_IPV4,
				G_SOCKET_TYPE_DATAGRAM, 0, &error);
		if (error) {
			goto err;
		}
		//g_socket_set_timeout(socket, 2);
		addr = g_inet_socket_address_new_from_string("0.0.0.0", local_port);
		g_socket_bind(socket, addr, FALSE, &error);
		if (error) {
			goto err;
		}
		priv->socket = socket;
		g_thread_new("server", read_thread, win);
		//g_signal_connect (socket, "incoming",
			//	G_CALLBACK (incoming_read_write_async_cb), win);
	} else if (!g_strcmp0(protocol, "tcp_client")) {
		if (!g_strcmp0(connect, TMP_BUTTON_DISCONNECT)) {
			conn = priv->socket;
			socket = g_socket_connection_get_socket(conn);
			rc = g_socket_close(socket, &error);
			if (rc == TRUE) {
						TMP_BUTTON_CONNECT);
			}
		} else if (!g_strcmp0(connect, TMP_BUTTON_CONNECT)) {
			client = g_socket_client_new ();
		//	g_socket_client_set_timeout(client, 2);
			addr_s = g_network_address_new (ip, port);
			conn = g_socket_client_connect(client, addr_s, NULL, &error);
			if (error) {
				goto err;
			}
			priv->socket = conn;
			g_thread_new("client", read_thread, win);
			gtk_label_set_text(GTK_LABEL(priv->status), "connected");
					TMP_BUTTON_DISCONNECT);
		}
	} else if (!g_strcmp0(protocol, "tcp_server")) {
		if (!g_strcmp0(status, TMP_STATUS_OPENED)) {
			//TODO
			g_socket_service_stop(priv->socket);
			g_socket_listener_close(priv->socket);
			g_object_unref(priv->socket);
			priv->socket = NULL;
			gtk_label_set_text(GTK_LABEL(priv->status),
					TMP_STATUS_CLOSED);
					TMP_BUTTON_OPEN);
		} else {
			service = g_socket_service_new();
			//service = g_threaded_socket_service_new (-1);
			g_socket_listener_add_inet_port(G_SOCKET_LISTENER(service),
					local_port, NULL, &error);
			if (error) {
				goto err;
			}
			//g_signal_connect(service, "run", G_CALLBACK(handler), win);
			g_signal_connect(service, "incoming",
					G_CALLBACK(incoming_cb), win);
			priv->socket = service;
			gtk_label_set_text(GTK_LABEL(priv->status),
					TMP_STATUS_OPENED);
					TMP_BUTTON_CLOSE);
		}
	} else {
		g_print("invalid protocol:%s\n", protocol);
		goto err;
	}
	//g_socket_client_connect_async(client, addr_s, NULL, connected_cb, win);
err:
	if (error) {
		g_print ("%s\n", error->message);
		gtk_label_set_text(GTK_LABEL(priv->status), error->message);
		g_error_free (error);
	}
	return;
}
*/
static void
post_window_init (PostWindow *win)
{
	PostWindowPrivate *priv;
  GtkTextBuffer *buffer = NULL;
	//const gchar *input = NULL;

	gtk_window_set_title(GTK_WINDOW(win), "8300post");
	gtk_window_move(GTK_WINDOW(win), 400, 200);

	priv = post_window_get_instance_private (win);
	priv->settings = g_settings_new ("org.gtk.8300.post");

	gtk_widget_init_template (GTK_WIDGET (win));

	g_signal_connect (priv->enter, "clicked", G_CALLBACK (enter_cb), win);
  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(priv->input));
  g_settings_bind (priv->settings, "url", priv->url, "text",
                   G_SETTINGS_BIND_DEFAULT);
  g_settings_bind (priv->settings, "input", buffer, "text",
                   G_SETTINGS_BIND_DEFAULT);
  g_settings_bind (priv->settings, "method", priv->method, "active-id",
                   G_SETTINGS_BIND_DEFAULT);

   // g_free (data);
    //g_object_unref (provider);

	return;
}

static void
post_window_dispose (GObject *object)
{
	G_OBJECT_CLASS (post_window_parent_class)->dispose (object);
}

static void
post_window_class_init (PostWindowClass *class)
{
	G_OBJECT_CLASS (class)->dispose = post_window_dispose;

	gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (class),
					       "/org/gtk/8300/post/postwin.ui");
	gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class),
			PostWindow, method);
	gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class),
			PostWindow, url);
	gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class),
			PostWindow, input);
	gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class),
			PostWindow, msg_box);
	gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class),
			PostWindow, msg_scrolled);
	gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class),
			PostWindow, enter);
	gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class),
			PostWindow, status);
	gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class),
			PostWindow, hex);
}

PostWindow *
post_window_new (Post *app)
{
  return g_object_new (EXAMPLE_APP_WINDOW_TYPE, "application", app, NULL);
}

void
post_window_open (PostWindow *win,
                         GFile            *file)
{
	/*
	PostWindowPrivate *priv;
	gchar *basename;
	GtkWidget *scrolled, *view;
	gchar *contents;
	gsize length;
	GtkTextBuffer *buffer;
	GtkTextTag *tag;
	GtkTextIter start_iter, end_iter;

	priv = post_window_get_instance_private (win);
	*/
}
